package com.sojpt.boot;

import java.sql.Connection;
import java.sql.SQLException;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import com.jfinal.kit.LogKit;
import com.jfinal.plugin.activerecord.ActiveRecordException;
import com.jfinal.plugin.activerecord.Config;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.NestedTransactionHelpException;


@Aspect
@Component
@Order(10)
public class TxAnnotationAspectConfig {

    /**
     * *匹配带有 AdminOnlyMethod Annotation的方法
     */
    @Pointcut("@annotation(com.sojpt.boot.Tx)")
    public void txConfigAnnotation(){
    	
    }
    
    @Around("txConfigAnnotation() && @annotation(txConfig)")
    public Object around(ProceedingJoinPoint jp, Tx txConfig) throws Throwable{
    	
    	Object obj = null;
    	
		Config config = getConfigWithTxConfig(txConfig);
		if (config == null)
			config = DbKit.getConfig();
		
		Connection conn = config.getThreadLocalConnection();
		if (conn != null) {	// Nested transaction support
			try {
				if (conn.getTransactionIsolation() < getTransactionLevel(config, txConfig))
					conn.setTransactionIsolation(getTransactionLevel(config, txConfig));
				obj = jp.proceed(jp.getArgs());
			} catch (SQLException e) {
				throw new ActiveRecordException(e);
			}
		}
    	
		Boolean autoCommit = null;
		try {
			conn = config.getConnection();
			autoCommit = conn.getAutoCommit();
			config.setThreadLocalConnection(conn);
			conn.setTransactionIsolation(getTransactionLevel(config, txConfig));	// conn.setTransactionIsolation(transactionLevel);
			conn.setAutoCommit(false);
			obj = jp.proceed(jp.getArgs());
			conn.commit();
		} catch (NestedTransactionHelpException e) {
			if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
			LogKit.logNothing(e);
		} catch (Throwable t) {
			if (conn != null) try {conn.rollback();} catch (Exception e1) {LogKit.error(e1.getMessage(), e1);}
			
			// 支持在 controller 中 try catch 的 catch 块中使用 render(...) 并 throw e，实现灵活控制 render
			/**
			if (inv.isActionInvocation() && inv.getController().getRender() != null) {
				LogKit.error(t.getMessage(), t);
			} else {
				throw t instanceof RuntimeException ? (RuntimeException)t : new ActiveRecordException(t);
			}
			*/
		}
		finally {
			try {
				if (conn != null) {
					if (autoCommit != null)
						conn.setAutoCommit(autoCommit);
					conn.close();
				}
			} catch (Throwable t) {
				LogKit.error(t.getMessage(), t);	// can not throw exception here, otherwise the more important exception in previous catch block can not be thrown
			}
			finally {
				config.removeThreadLocalConnection();	// prevent memory leak
			}
		}
		
    	return obj;
    }
    
	private Config getConfigWithTxConfig(Tx txConfig) {
		
		if (txConfig.value().equals("")) return null;

		Config config = DbKit.getConfig(txConfig.value());
		if (config == null)
			throw new RuntimeException("Config not found with TxConfig: " + txConfig.value());
		return config;
	}
	
	protected int getTransactionLevel(Config config, Tx txConfig) {
		if (txConfig.level() == 0) {			
			return config.getTransactionLevel();
		} else {
			return txConfig.level();
		}
	}
}